#include "Triangle.h"
#include "cmath.h"
namespace RayTrace{

	CTriangle::CTriangle( const vector3& p1,const vector3& p2,const vector3& p3 )
		:v1(p1),v2(p2),v3(p3),normal(vector3::ZeroUnit)
	{

	}
	CTriangle::~CTriangle(void)
	{
	}
	//double CTriangle::InterSectionWithRay( const Ray& inRay,CTriangle::IntSectInf& inf )
	//{
	//	//vector3 N=vector3::Cross(v2-v1,v3-v1);
	///*	vector3 N=GetNormal();
	//	double d=-vector3::Dot(N,v1);
	//	double dist=-(d+vector3::Dot(inRay.origin,N))/vector3::Dot(inRay.direction,N);*/
	//	double dist=DistRayThoughPlane(inRay);
	//	
	//	if ((dist < MMIN) || (dist > MMAX)) {
	//		return -1;//negative indicates miss
	//	}
	//	else{
	//		vector3 interPos=inRay.origin+(inRay.direction)*dist;
	//		
	//		//barycentric coordinate

	//		if (GetBaryCentricCoordinate(interPos,v1,v2,v3,inf.barycentric))
	//		{
	//			if (inf.barycentric.x<0 ||inf.barycentric.y<0 ||inf.barycentric.z<0 )
	//			{
	//				return -1;
	//			}
	//			else
	//			{
	//				return dist;
	//			}
	//		}	
	//		else
	//		{
	//			return -1;
	//		}
	//
	//	}

	//}


	bool CTriangle::GetBaryCentricCoordinate( const vector3& vi, const vector3& v1,const vector3& v2,const vector3& v3,vector3& barycentric )
	{
		float x=vi.x;
		float y=vi.y;
		float z=vi.z;

		float x1=v1.x;
		float y1=v1.y;
		float z1=v1.z;

		float x2=v2.x;
		float y2=v2.y;
		float z2=v2.z;

		float x3=v3.x;
		float y3=v3.y;
		float z3=v3.z;

		vector3 N=GetNormal();
		int axis=0;//1,2,3 stand for X Y Z respectively
		if (fabs( N.x ) > fabs( N.y ))
			if (fabs( N.x ) > fabs( N.z )) axis = 1;
			else axis = 3;

		else
			if (fabs( N.y ) > fabs( N.z )) axis = 2; 
			else axis = 3;

		float A=0,B=0,C=0,D=0,E=0,F=0;
			switch(axis)
			{
			case 1:
				A=y1-y3;
				B=y2-y3;
				C=z1-z3;
				D=z2-z3;
				E=y-y3;
				F=z-z3;
				break;
			case 2:
				A=x1-x3;
				B=x2-x3;
				C=z1-z3;
				D=z2-z3;
				E=x-x3;
				F=z-z3;
				break;
			case 3:
				A=x1-x3;
				B=x2-x3;
				C=y1-y3;
				D=y2-y3;
				E=x-x3;
				F=y-y3;
				break;
			default:
				break;
			}
		if (CMath::LinearEquation(A,B,C,D,E,F,barycentric.x,barycentric.y))
		{
			barycentric.z=1-(barycentric.x+barycentric.y);
			return true;
		}
		else
		{
			return false;
		}
	}

	double CTriangle::DistRayThroughPlane( const Ray& inRay)
	{
		vector3 N=GetNormal();
		double d=-vector3::Dot(N,v1);
		double dist=-(d+vector3::Dot(inRay.origin,N))/vector3::Dot(inRay.direction,N);
		return dist;
	}
	const vector3& CTriangle::GetNormal()
	{
		if (fabs(normal.x)<MMIN &&fabs(normal.y)<MMIN &&fabs(normal.z)<MMIN )
		{
			normal=vector3::Cross(v2-v1,v3-v1);
			return normal;
		}
		else
		{
			return normal;
		}
	}

	bool CTriangle::InArea( const vector3& point,IntSectInf& inf )
	{
		if (GetBaryCentricCoordinate(point,v1,v2,v3,inf.barycentric))
		{
			if (inf.barycentric.x<0 ||inf.barycentric.y<0 ||inf.barycentric.z<0 )
			{
				return false;
			}
			else
			{
				return true;
			}
		}	
		else
		{
			return false;
		}
	}

	bool CTriangle::InArea( const vector3& point )
	{
		vector3 barycentric;
		if (GetBaryCentricCoordinate(point,v1,v2,v3,barycentric))
		{
			if (barycentric.x<0 ||barycentric.y<0 ||barycentric.z<0 )
			{
				return false;
			}
			else
			{
				return true;
			}
		}	
		else
		{
			return false;
		}
	}
	bool CTriangle::IsThroughByRay( const Ray&ray,double t )
	{
		double tmpdist=DistRayThroughPlane(ray);
		if (tmpdist<t)
		{
			if (InArea(ray.GetPos(tmpdist)))
			{
				return true;
			}

		}
		return false;
	}

	const vector3& CTriangle::operator[](int n)const 
	{
		switch(n)
		{
		case 0:
			return v1;
			break;
		case 1:
			return v2;
			break;
		case 2:
			return v3;
		    break;
		default:
			return vector3::ZeroUnit;
		    break;
		}
	}
}